<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Scripts\Tools;

use ReflectionClass;
use Srv\Apps\Apis\Common\Ctl\ToUrlCtl;
use Srv\Apps\Proto\Enum\ErrCodeEnum;
use Srv\Apps\Proto\Enum\LangKeyEnum;
use Srv\Apps\Proto\Enum\ResultTypeEnum;
use Srv\Apps\Proto\Enum\SocketEventTypeEnum;
use Srv\Libs\Boot;
use Srv\Libs\Common\CommArray;
use Srv\Libs\Common\CommFile;
use Srv\Libs\Common\CommJson;
use Srv\Libs\Common\CommNet;
use Srv\Libs\Common\CommString;
use Srv\Libs\Common\CommTime;
use Srv\Libs\Common\CommValid;
use Srv\Libs\ConfModel\RouteConf;
use Srv\Libs\Frame\Conf;
use Srv\Libs\Frame\OAbstract;
use Srv\Libs\Frame\ResponseMain;
use Srv\Libs\Frame\Route;
use Srv\Libs\Tools\Http;
use Srv\Libs\Tools\ReflectionClassData;
use Srv\Libs\Tools\ShellPrint;

require_once dirname(__DIR__, 2).'/Libs/Boot.php';
Boot::runConf('');

// 更新文档
$docApiHttp             = 'http://api.dev.blbx.com/server/index.php?s=/api/item/updateByApi';
$docApiKey              = 'f198299071e168a45c5bfb23b2c72a331266233608';
$docApiToken            = '7922d03bf8728eb40ef7334c6d6d44461149926054';
// 跳过组名列表
$groupNameSkipList      = [
    'webs',
];

// 获取反射类MD内容
function getRefClassMdString(string $className, string &$comment, array &$classNameExtList, array $classNameParentList = [], string $indexPrefix = ''):string
{
    if(count($classNameParentList) < 1) $classNameParentList = [$className];
    $mdStr              = '';
    $refClassData       = ReflectionClassData::getRefDataByProto($className);
    $refClassData['comment'] = str_replace('&#64;', '@', $refClassData['comment']);
    $comment            = $refClassData['comment'];
    if(!isset($refClassData['propertyList']) || count($refClassData['propertyList']) < 1) return $mdStr;
    if(strlen($indexPrefix) < 1){
        $mdStr          .= "\n".'######<span id=\''.$refClassData['nameOnly'].'\' />`'.$refClassData['nameOnly'].'` '.$refClassData['comment']."\n";
        $mdStr          .= '| Num | Name | Type | Must | Remark |'."\n".'|:--|:-----|:----|:--:|:------|'."\n";
    }
    $classNameCurrList  = [];
    foreach($refClassData['propertyList'] as $propertyName => $propertySet){
        if(preg_match_all('/\[([a-zA-Z]+(VO|DTO))\]/u', $propertySet['comment'], $propertyMatch)){
            $pos                        = strrpos($className, '\\');
            if($pos > 0){
                $classNameNamespace     = substr($className, 0, $pos+1);
                foreach ($propertyMatch[1] as $propertyClassNameOnly){
                    if(strtolower($propertyClassNameOnly) === 'optional') continue;
                    $classNameExtList[] = $classNameNamespace.$propertyClassNameOnly;
                }
            }
        }
        $isMust         = strpos(strtolower($propertySet['comment']), '[optional]') === false;
        $isArray        = strpos($propertySet['flag'], 'repeated') !== false;
        $type           = $propertySet['type'];
        $index          = $propertySet['index'];
        $subType        = '';
        if(strpos($type, '\\') !== false){
            if(preg_match('/^google\\\\protobuf\\\\[A-Za-z]+/', $type)){
                $typeShow   = '`'.($isArray?'List&lt;`'.$type.'`&gt;':$type).'`';
            }else{
                $typeOnly   = CommString::getClassOnlyName($type);
                $typeShow   = '*'.($isArray?'List&lt; '.$typeOnly.' &gt;':$typeOnly);
                if(substr($type, -4) === 'Enum'){
                    if(!in_array($type, $classNameExtList, true)) $classNameExtList[] = $type;
                }else{
                    if(!in_array($type, $classNameParentList, true)){
                        $subType                = $type;
                        if(!in_array($type, $classNameCurrList, true)) $classNameCurrList[] = $type;
                    }
                }
            }
        }else{
            $typeShow       = $isArray?'List&lt; '.$type.' &gt;':$type;
        }
        $propertySet['comment'] = str_replace('&#64;', '@', $propertySet['comment']);
        $propertySet['comment'] = str_replace('@[optional]', '', $propertySet['comment']);
        $propertySet['comment'] = str_replace('[optional]', '', $propertySet['comment']);
        $commentShow            = @preg_replace(['/(@?)\[/', '"]"'], [' `$1[', ']`'], $propertySet['comment']);
        $mdStr                  .= '| '.$indexPrefix.(strlen($indexPrefix) > 0 ? '.' : '').$index.(strlen($subType) > 0 ? ' －' : '').' | '.(strlen($indexPrefix) > 0 ? implode('', array_fill(0, count(explode('.', $indexPrefix)), '　')).'├─'.implode('', array_fill(0, count(explode('.', $indexPrefix)), '')) : '').$propertyName.' | '.(strlen($typeShow) > 0 ? $typeShow : '-').' | '.($isMust?'Yes':'No').' | '.(strlen($commentShow) > 0 ? $commentShow : '-').' |'."\n";
        if(strlen($subType) > 0){
            $mdStr              .= getRefClassMdString($subType, $comment, $classNameExtList, array_unique(array_merge($classNameParentList, $classNameCurrList)), $indexPrefix.(strlen($indexPrefix) > 0 ? '.' : '').$index);
        }
    }
    return $mdStr;
}

// 获取反射枚举类MD内容
function getRefEnumMdString(string $className, string &$comment):string
{
    $mdStr              = '';
    $refClassData       = ReflectionClassData::getRefDataByProto($className);
    $refClassData['comment'] = str_replace('&#64;', '@', $refClassData['comment']);
    $comment            = $refClassData['comment'];
    if(!isset($refClassData['constantList']) || count($refClassData['constantList']) < 1) return $mdStr;
    $mdStr              .= "\n".'######<span id=\''.$refClassData['nameOnly'].'\' />`'.$refClassData['nameOnly'].'` '.$refClassData['comment']."\n";
    $mdStr              .= '| Value | Name | Remark |'."\n".'|:--:|:----|:------|'."\n";
    foreach($refClassData['constantList'] as $propertyName => $propertySet){
        if(substr($propertyName, 0, 1) === '_' || !isset($propertySet['value']) || !is_numeric($propertySet['value'])) continue;
        $commentShow    = @preg_replace(['/(@?)\[/', '"]"'], [' `$1[', ']`'], $propertySet['comment']);
        $mdStr         .= '| '.$propertySet['value'].' | '.$propertyName.' | '.$commentShow.' |'."\n";
    }
    return $mdStr;
}

// 获取proto文件MD内容
function getProtoFileList(array $fileList):string
{
    $mdStr              = '';
    if(count($fileList) < 1) return $mdStr;
    foreach($fileList as $filePath){
        if(!is_string($filePath)) continue;
        $fileContent    = file_get_contents($filePath);
        $fileContent    = preg_replace('/syntax[^\n]+\n/i', '', $fileContent);
        $fileContent    = preg_replace('/package[^\n]+\n/i', '', $fileContent);
        $fileContent    = preg_replace('/import[^\n]+\n/i', '', $fileContent);
        $fileContent    = trim($fileContent);
        if(strlen($fileContent) < 6) continue;
        $mdStr          .= '````protobuf'."\n".$fileContent."\n".'````'."\n";
    }
    return $mdStr;
}
// 生成示例数据
function genEgJsonData(string $className):array
{
    $egJsonData         = [];
    if(!CommValid::isExistsClass($className)) return $egJsonData;
    $classNameObject    = new $className();
    if(!($classNameObject instanceof OAbstract)) return $egJsonData;
    return $classNameObject->getOMessageJsonArrayForDefault();
}

// 获取请求Json内容
function getReqEgJsonStr(string $classNameDTO):string
{
    $reqEgStr       = '';
    $egJsonData     = genEgJsonData($classNameDTO);
    if(count($egJsonData) > 0) $reqEgStr = CommJson::encodeArrayPretty($egJsonData);
    return $reqEgStr;
}

// 获取响应Json内容
function getRspEgJsonStr(string $classNameVO):string
{
    $rspEgStr       = '';
    $egJsonData     = genEgJsonData($classNameVO);
    if(count($egJsonData) > 0) $rspEgStr = CommJson::encodeArrayPretty($egJsonData);
    return $rspEgStr;
}

$currTimeStr    = CommTime::getString();
$apiDocTpl      = <<<EOF_DOC_TPL
**时间：** `{{TIME_STR}}`
**描述：** `{{COMMENT_STR}}`
**权限：** `{{AUTH_STR}}`
**错误码：** {{CODE_STR}}

**参数：** {{REQ_STR}}

**响应：** {{RSP_STR}}

**Err响应：** {{ERR_RSP_STR}}

**扩展：** {{EXT_STR}}

**参数示例：** 
````json
{{REQ_EG_STR}}
````

**响应示例：** 
````json
{{RSP_EG_STR}}
````
EOF_DOC_TPL;
$routeList                      = Route::initData();
$pathAllList                    = [];
foreach ($routeList as $routeGroupList){
    foreach ($routeGroupList as $groupName => $pathList){
        if(in_array($groupName, $groupNameSkipList, true)) continue;
        ksort($pathList);
        $pageNum                = 0;
        foreach ($pathList as $pathUrl => $pathData){
            $path               = ltrim($pathUrl, '/');
            $class              = $pathData[0];
            $method             = $pathData[1];
            unset($class, $method);
            $auth               = $pathData[2];
            $code               = $pathData[3];
            $DTO                = $pathData[4];
            $VO                 = $pathData[5];
            $Comment            = $pathData[6];
            $FlagList           = $pathData[7];
            $ErrVOList          = $pathData[8];
            $limitList          = $pathData[9];
            $logText            = str_pad($groupName.':'.$path, 39, ' ', STR_PAD_RIGHT);
            $logText            .= str_pad($auth, 5, ' ', STR_PAD_RIGHT);
            $logText            .= str_pad($code, 13, ' ', STR_PAD_RIGHT);
            $logText            .= str_pad($DTO, 76, ' ', STR_PAD_LEFT);
            $logText            .= str_pad($VO, 60, ' ', STR_PAD_LEFT);
            $logText            .= ' '.str_pad($Comment, 42, ' ', STR_PAD_RIGHT);
            $logText            .= "\n";
            if(in_array('hide', $FlagList, true) || in_array('HIDE', $FlagList, true)){ // 隐藏
                ShellPrint::printSuccessString($logText);
                continue;
            }
            $codeStr                    = '';
            if(strlen($code) > 0){
                $tableDataList          = [['Value', 'Name'], ['--:', ':--']];
                foreach (explode(',', $code) as $codeNum) $tableDataList[] = [$codeNum, ErrCodeEnum::getName(intval($codeNum))];
                $codeStr                .= "\n";
                foreach ($tableDataList as $tableDataLine) $codeStr .= "\n".'| '.implode(' | ', $tableDataLine).' |';
            }
            $classNameRefList           = [];
            $extStr                     = '';
            $reqStr                     = '';
            if(strlen($DTO) > 0){
                $classRefItemList       = [$DTO];
                while(count($classRefItemList) > 0){
                    $className          = array_shift($classRefItemList);
                    if(in_array($className, $classNameRefList, true)) continue;
                    $classNameRefList[] = $className;
                    $comment            = '';
                    $classNameExtList   = [];
                    if(substr($className, -4) === 'Enum'){
                        if(strlen($reqStr) > 0){
                            $extStr     .= getRefEnumMdString($className, $comment);
                        }else{
                            $reqStr     .= getRefEnumMdString($className, $comment);
                        }
                    }else{
                        $reqStr         .= getRefClassMdString($className, $comment, $classNameExtList, []);
                    }
                    if(count($classNameExtList) > 0) array_unshift($classRefItemList, ...$classNameExtList);
                }
            }
            $rspStr                     = '';
            if(strlen($VO) > 0){
                $classRefItemList       = [$VO];
                while(count($classRefItemList) > 0){
                    $className          = array_shift($classRefItemList);
                    if(in_array($className, $classNameRefList, true)) continue;
                    $classNameRefList[] = $className;
                    $comment            = '';
                    $classNameExtList   = [];
                    if(substr($className, -4) === 'Enum'){
                        if(strlen($rspStr) > 0){
                            $extStr     .= getRefEnumMdString($className, $comment);
                        }else{
                            $rspStr     .= getRefEnumMdString($className, $comment);
                        }
                    }else{
                        $rspStr         .= getRefClassMdString($className, $comment, $classNameExtList, []);
                    }
                    if(count($classNameExtList) > 0) array_unshift($classRefItemList, ...$classNameExtList);
                }
            }
            $errRspStr                  = '';
            if(count($ErrVOList) > 0){
                $classRefItemList       = $ErrVOList;
                while(count($classRefItemList) > 0){
                    $className          = array_shift($classRefItemList);
                    if(in_array($className, $classNameRefList, true)) continue;
                    $classNameRefList[] = $className;
                    $comment            = '';
                    $classNameExtList   = [];
                    if(substr($className, -4) === 'Enum'){
                        if(strlen($rspStr) > 0){
                            $extStr     .= getRefEnumMdString($className, $comment);
                        }else{
                            $errRspStr  .= getRefEnumMdString($className, $comment);
                        }
                    }else{
                        $errRspStr      .= getRefClassMdString($className, $comment, $classNameExtList, []);
                    }
                    if(count($classNameExtList) > 0) array_unshift($classRefItemList, ...$classNameExtList);
                }
            }
            $reqEgStr           = getReqEgJsonStr($DTO);
            $rspEgStr           = getRspEgJsonStr($VO);
            $search             = ['"{{TIME_STR}}"', '"{{COMMENT_STR}}"', '"{{AUTH_STR}}"', '"{{GROUP_STR}}"', '"{{REQ_STR}}"', '"{{RSP_STR}}"', '"{{ERR_RSP_STR}}"', '"{{CODE_STR}}"', '"{{REQ_EG_STR}}"', '"{{RSP_EG_STR}}"', '"{{EXT_STR}}"'];
            $replace            = [$currTimeStr, $Comment, $auth, $groupName, strlen($reqStr) < 1 ? '`无`' : $reqStr, strlen($rspStr) < 1 ? '`无`' : $rspStr, strlen($errRspStr) < 1 ? '`无`' : $errRspStr, strlen($codeStr) < 1 ? '`无`' : $codeStr, $reqEgStr, $rspEgStr, strlen($extStr) < 1 ? '`无`' : $extStr];
            $pageContent        = @preg_replace($search, $replace, $apiDocTpl);
            $pageContent        = @preg_replace('/{{\S+}}/', '', $pageContent);
            $pageTitle          = trim(ltrim(trim($path), '/'));
            $pageTitleSplit     = explode('/', $pageTitle);
            $catNameOne         = $pageTitleSplit[0]??'';
            if(!isset($pageTitleSplit[1])) $catNameOne = '/';
            $catNameTwo         = isset($pageTitleSplit[2]) ? $pageTitleSplit[1]??'' : '';
            $catNameTwo         = strlen($catNameTwo) > 0 ? $catNameOne.'/'.$catNameTwo : '';
            $postData           = [
                'api_key'       => $docApiKey,
                'api_token'     => $docApiToken,
                'cat_name'      => strlen($catNameOne) < 1 ? '' : ($groupName.':'.($catNameOne === '/' ? $catNameOne : '/'.$catNameOne)),
                'cat_name_sub'  => strlen($catNameTwo) < 1 ? '' : ($groupName.':'.'/'.$catNameTwo),
                'page_title'    => $groupName.':/'.$pageTitle,
                'page_content'  => $pageContent,
                's_number'      => ++$pageNum,
            ];
            if(strlen($postData['cat_name']) < 1) unset($postData['cat_name']);
            if(strlen($postData['cat_name_sub']) < 1) unset($postData['cat_name_sub']);
            $param              = ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => $postData];
            $urlInfo            = CommNet::getUrlInfo($docApiHttp, $param);
            $body               = trim($urlInfo['body'] ?? '');
            $resData            = CommJson::decodeArray($body);
            if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
                $pageId         = intval($resData['data']['page_id']??0);
                $pathKey        = $postData['page_title'];
                $pathAllList[$pathKey] = [
                    'groupName'     => $groupName,
                    'pageTitle'     => $pageTitle,
                    'pathStr'       => $path,
                    'authStr'       => $auth,
                    'codeStr'       => $code,
                    'DTOStr'        => $DTO,
                    'VOStr'         => $VO,
                    'Comment'       => $Comment,
                    'pageId'        => $pageId,
                ];
                ShellPrint::printSuccessString($logText);
            }else{
                ShellPrint::printErrorString($logText.print_r($resData, true)."\n");
            }
        }
    }
}

// IndexList
$indexListContent           = '**时间：** `'.$currTimeStr.'`'."\n";
$indexListContent           .= "\n".'| Group | Path | Auth | Code | Comment |'."\n".'|:--:|:--|:--:|:--:|:--|'."\n";
$groupStrLast               = '';
if(count($pathAllList) > 0) foreach ($pathAllList as $pathKey => $pathItem){
    list($groupStr, $pathStr) = explode(':', $pathKey, 2);
    $pageId                 = $pathItem['pageId'];
    if(strlen($groupStrLast) < 1) $groupStrLast = $groupStr;
    if($groupStrLast !== $groupStr){
        $groupStrLast       = $groupStr;
        $indexListContent   .= '| - | - | - | - | - |'."\n";
    }
    $commentShow            = @preg_replace(['"\["', '"]"'], [' `[', ']`'], $pathItem['Comment']);
    if($pageId > 0){
        $linkUrl            = './?s=/Home/Page/index/page_id/'.$pageId;
        $indexListContent   .= '| '.$groupStr.' |  ['.$pathStr.']('.$linkUrl.') | '.$pathItem['authStr'].' | '.$pathItem['codeStr'].' | '.$commentShow.' |'."\n";
    }else{
        $indexListContent   .= '| '.$groupStr.' |  '.$pathStr.' | '.$pathItem['authStr'].' | '.$pathItem['codeStr'].' | '.$commentShow.' |'."\n";
    }
}
$pageTitleShow      = 'IndexList';
$urlInfo            = CommNet::getUrlInfo($docApiHttp, ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => ['page_title' => $pageTitleShow, 'page_content' => $indexListContent, 'api_key' => $docApiKey, 'api_token' => $docApiToken]]);
$body               = trim($urlInfo['body'] ?? '');
$resData            = CommJson::decodeArray($body);
if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
    ShellPrint::printlnString($pageTitleShow, ShellPrint::COLOR_PURPLE);
}else{
    ShellPrint::printErrorString($pageTitleShow.print_r($resData, true)."\n");
}

// 请求头配置
$routeListContent           = '**时间：** `'.$currTimeStr.'`'."\n";
$accessHeaderList           = ResponseMain::getAccessHeaderList();
$refClassData               = ReflectionClassData::getRefData(ResponseMain::class);
$tableDataList              = [['Name', 'Value', 'Remark'], [':--', ':--', ':--']];
foreach ($accessHeaderList as $accessHeaderName){
    $accessHeaderKeyHeader  = 'HEADER_'.Http::getHeaderName($accessHeaderName);
    $columnComment          = $refClassData['constantList'][$accessHeaderKeyHeader]['comment']??'';
    $tableDataList[]        = [strtolower($accessHeaderName), '-', $columnComment];
}
$routeListContent           .= "\n**请求头配置**\n";
foreach ($tableDataList as $tableDataLine){
    $routeListContent       .= "\n".'| '.implode(' | ', $tableDataLine).' |';
}
$routeListContent           .= "\n";
// 路由配置
$routeListContent           .= "\n**路由配置**\n";
$RouteConfGroup             = Conf::getRouteConfGroup();
$RouteConfList              = $RouteConfGroup->getGroupNameList();
if(count($RouteConfList) > 0){
    $routeDataTpl           = [
        'groupName'             => '',
        'appKey'                => '',
        'reqHeaderFromGet'      => '',
        'reqHeaderFromCookie'   => '',
        'reqHeaderCheck'        => '',
        'reqContentSig'         => '',
        'reqContentSecret'      => '',
        'reqContentType'        => '',
        'webSocket'             => '',
        'webSocketAuth'         => '',
        'rspContentSig'         => '',
        'rspContentSecret'      => '',
        'rspContentType'        => '',
    ];
    $routeDataList          = array_fill(0, count($RouteConfList), $routeDataTpl);
    $RouteConfPropertyList  = [];
    $routeDataNum           = -1;
    foreach ($RouteConfList as $RouteConf){
        ++$routeDataNum;
        if(!($RouteConf instanceof RouteConf)) continue;
        if(count($RouteConfPropertyList) < 1) $RouteConfPropertyList  = $RouteConf->getPropertyList();
        $RouteConfData          = CommArray::objectToArray($RouteConf);
        foreach ($routeDataList[$routeDataNum] as $routeDataKey => &$routeDataVal){
            if(!isset($RouteConfData[$routeDataKey])) continue;
            if(is_bool($RouteConfData[$routeDataKey])){
                $routeDataVal   = $RouteConfData[$routeDataKey] ? 'TRUE' : 'FALSE';
            }else{
                $routeDataVal   = $RouteConfData[$routeDataKey];
            }
            if($routeDataKey === 'groupName'){
                $routeDataVal   = '`'.$routeDataVal.'`';
            }else{
                if(strlen($routeDataVal) > 0 && !in_array($routeDataVal, ['TRUE', 'FALSE', 'NONE', 'RAND', 'FROM', 'JSON', 'XML', 'TEXT', 'HTML', 'PROTOBUF', 'BINARY'], true)) $routeDataVal = '`已配置`';
            }
        }
        unset($routeDataVal);
    }
    $tableDataList              = [['Name'], [':--']];
    foreach ($routeDataTpl as $routeDataTplKey => $routeDataTplVal){
        if($routeDataTplKey === 'groupName') continue;
        $tableDataList[]        = [$routeDataTplKey];
    }
    $tableColNum                = 0;
    foreach ($routeDataList as $routeDataVal){
        if(!isset($routeDataVal['groupName'])) continue;
        ++$tableColNum;
        $groupName              = $routeDataVal['groupName'];
        $tableDataList[0][$tableColNum] = $groupName;
        $tableDataList[1][$tableColNum] = ':--:';
        foreach ($tableDataList as $tableDataKey => $tableDataVal){
            if($tableDataKey < 2) continue;
            $columnName         = $tableDataVal[0];
            $tableDataList[$tableDataKey][$tableColNum] = $routeDataVal[$columnName];
        }
    }
    ++$tableColNum;
    $tableDataList[0][$tableColNum] = 'Remark';
    $tableDataList[1][$tableColNum] = ':--';
    foreach ($tableDataList as $tableDataKey => $tableDataVal){
        if($tableDataKey < 2) continue;
        $columnName         = $tableDataVal[0];
        $columnComment      = @preg_replace(['/(@?)\[/', '"]"'], [' `$1[', ']`'], $RouteConfPropertyList[$columnName]['comment']??'');
        $tableDataList[$tableDataKey][$tableColNum] = $columnComment;
    }
    foreach ($tableDataList as $tableDataLine){
        $routeListContent       .= "\n".'| '.implode(' | ', $tableDataLine).' |';
    }
    $routeListContent           .= "\n";
}
// 令牌配置
$TokenKeyConf           = Conf::getTokenKeyConf();
$tokenKeyData           = [
    'appId'                 => '',
    'renewTime'             => '',
    'expireTime'            => '',
    'checkCache'            => '',
    'checkDb'               => '',
    'validDevId'            => '',
    'validDevPlatform'      => '',
];
$TokenKeyConfPropertyList   = $TokenKeyConf->getPropertyList();
$TokenKeyConfData           = CommArray::objectToArray($TokenKeyConf);
foreach ($tokenKeyData as $tokenKeyKey => &$tokenKeyVal){
    if(!isset($TokenKeyConfData[$tokenKeyKey])) continue;
    if(is_bool($TokenKeyConfData[$tokenKeyKey])){
        $tokenKeyVal    = $TokenKeyConfData[$tokenKeyKey] ? 'TRUE' : 'FALSE';
    }else{
        $tokenKeyVal    = $TokenKeyConfData[$tokenKeyKey];
    }
}
unset($tokenKeyVal);
$tableDataList              = [['Name', 'Value', 'Remark'], [':--', ':--:', ':--']];
foreach ($tokenKeyData as $tokenKeyKey => $tokenKeyVal){
    $columnComment      = @preg_replace(['/(@?)\[/', '"]"'], [' `$1[', ']`'], $TokenKeyConfPropertyList[$tokenKeyKey]['comment']??'');
    $tableDataList[]        = [$tokenKeyKey, $tokenKeyVal, $columnComment];
}
$routeListContent           .= "\n**令牌配置**\n";
foreach ($tableDataList as $tableDataLine){
    $routeListContent       .= "\n".'| '.implode(' | ', $tableDataLine).' |';
}
$routeListContent           .= "\n";
$routeListContent           .= <<<SIG_SECERT
**请求/响应的加密**
- 判断请求头中 `x-key` 的值,仅当=`NONCE`为不加密
- x-key: 规则 base64_encode( des_encode( rand_str(16) + 请求头的{x-app-nonce} ) )
- WebSocket 通信中为约定的固定密钥


**请求签名**
- header排序内容: 将请求头按key名升序排,并用`&`拼接, `key1=val1&key2=val2&key3=val3`
````
x-app-channel=official/com.exp.app
x-app-key=1234567890aBcDeF
x-app-nonce=4436522201939711
x-app-platform=android
x-app-token=123456789|xxxxxxxxxxxxxxxxxxxxxxxx
x-dev-id=AAAABBBB-CCCC-DDDD-EEEE-FF0123456111
x-dev-lang=zh
x-dev-location=113.247983,23.173851
x-dev-name=PD1981;vivo;V1981A
x-dev-net=wifi
x-dev-screen=750x1334
x-dev-system=Android 30
x-dev-time=1695107321689
x-dev-version=1.0.0.1
````
- header排序值为: x-app-channel=official/com.exp.app&x-app-key=1234567890aBcDeF&x-app-nonce=4436522201939711&x-app-platform=android&x-app-token=123456789|xxxxxxxxxxxxxxxxxxxxxxxx&x-dev-id=AAAABBBB-CCCC-DDDD-EEEE-FF0123456111&x-dev-lang=zh&x-dev-location=113.247983,23.173851&x-dev-name=PD1981;vivo;V1981A&x-dev-net=wifi&x-dev-screen=750x1334&x-dev-system=Android 30&x-dev-time=1695107321689&x-dev-version=1.0.0.1
- appSecret: 约定密钥
- bodyRaw: POST的Body原始内容
- x-sig规则: {header排序值} + {appSecret} + (未加密 ? {bodyRaw} : sha1( base64_encode( {bodyRaw} ) ) )


**响应签名**
- randNonceSha1: sha1( rand_str() ), (即: `随机生成的字符串计算sha1`)
- appSecret: 约定密钥
- secretKey: rand_str(16), (即: `随机生成的字符串用于Des加密的密钥`)
- headerXSig: 请求头的 `x-sig` 值
- headerXAppNonce: 请求头的 `x-app-nonce` 值
- headerXDevTime:请求头的 `x-dev-time` 值
- rspBodyRaw: 响应的Body原始内容
- rspContentSha1: 未加密 {rspBodyRaw}
- rspContentSha1: 加密时 sha1( base64_encode( {rspBodyRaw} ) ), rspBodyRaw为空时就是空无需base64和sha1
- x-sig规则: `长度为80字符` {randNonceSha1} + sha1({randNonceSha1} + ';' + {headerXSig} + ';' + sha1( 是否加密 ? {secretKey} : {appSecret} ) + ';' + {headerXAppNonce} + ';' + {headerXDevTime} + ';' + {rspContentSha1})
SIG_SECERT;
$routeListContent           .= "\n";
$pageTitleShow      = 'RouteToken';
$urlInfo            = CommNet::getUrlInfo($docApiHttp, ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => ['page_title' => $pageTitleShow, 'page_content' => $routeListContent, 'api_key' => $docApiKey, 'api_token' => $docApiToken]]);
$body               = trim($urlInfo['body'] ?? '');
$resData            = CommJson::decodeArray($body);
if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
    ShellPrint::printlnString($pageTitleShow, ShellPrint::COLOR_CYAN);
}else{
    ShellPrint::printErrorString($pageTitleShow.print_r($resData, true)."\n");
}

$pageNumDef         = 0;    // 页面顺序
// 定义枚举
$EnumClassNameList  = [
    ErrCodeEnum::class,
    ResultTypeEnum::class,
    LangKeyEnum::class,
];
if(count($EnumClassNameList) > 0){
    foreach ($EnumClassNameList as $EnumClassName){
        $comment            = '';
        $enumContent        = '**时间：** `'.$currTimeStr.'`';
        $enumContent        .= "\n".getRefEnumMdString($EnumClassName, $comment);
        $postData           = [
            'api_key'       => $docApiKey,
            'api_token'     => $docApiToken,
            'cat_name'      => '定义',
            'page_title'    => CommString::getClassOnlyName($EnumClassName),
            'page_content'  => $enumContent,
            's_number'      => ++$pageNumDef,
        ];
        $logText            = str_pad($EnumClassName, 64, ' ', STR_PAD_RIGHT);
        $logText            .= ' '.str_pad($comment, 42, ' ', STR_PAD_RIGHT);
        $logText            .= "\n";
        $param              = ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => $postData];
        $urlInfo            = CommNet::getUrlInfo($docApiHttp, $param);
        $body               = trim($urlInfo['body'] ?? '');
        $resData            = CommJson::decodeArray($body);
        if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
            ShellPrint::printSuccessString($logText);
        }else{
            ShellPrint::printErrorString($logText.print_r($resData, true)."\n");
        }
    }
}
// 定义Proto
$protoFilePathList  = [
    [Boot::getConfPath().'ProtoSrc/DTO/RequestDTO.proto',       'WS请求数据格式'],
    [Boot::getConfPath().'ProtoSrc/DTO/RequestExtDTO.proto',    '请求参数扩展'],
    [Boot::getConfPath().'ProtoSrc/VO/ResponseVO.proto',        '响应数据格式'],
    [Boot::getConfPath().'ProtoSrc/VO/ParamCheckErrorVO.proto', '检查参数错误'],
    [Boot::getConfPath().'ProtoSrc/VO/TokenVO.proto',           '令牌结构信息'],
];
if(count($protoFilePathList) > 0){
    foreach ($protoFilePathList as $protoFileItem){
        $protoFilePath      = $protoFileItem[0]??'';
        $protoFileName      = $protoFileItem[1]??'';
        if(!file_exists($protoFilePath)) continue;
        $protoContent       = '**时间：** `'.$currTimeStr.'`';
        $protoShowPath      = substr($protoFilePath, strrpos($protoFilePath, 'ProtoSrc'));
        if(preg_match('/^ProtoSrc\/(.*)\.proto$/', $protoShowPath, $match)){
            $classNameProto             = preg_replace('/\./', '\\', 'Srv.Apps.Proto.'.preg_replace('/\//', '\\', $match[1]));
            if(CommValid::isExistsClass($classNameProto)){
                $protoClassMdStr        = '';
                $classNameRefList       = [];
                $classRefItemList       = [$classNameProto];
                while(count($classRefItemList) > 0){
                    $className          = array_shift($classRefItemList);
                    if(preg_match('/^google\\\\protobuf\\\\[A-Za-z]+/', $className)) continue;
                    if(in_array($className, $classNameRefList, true)) continue;
                    $classNameRefList[] = $className;
                    $comment            = '';
                    $classNameExtList   = [];
                    if(substr($className, -4) === 'Enum'){
                        $protoClassMdStr    .= getRefEnumMdString($className, $comment);
                    }else{
                        $protoClassMdStr    .= getRefClassMdString($className, $comment, $classNameExtList, []);
                    }
                    if(count($classNameExtList) > 0) array_unshift($classRefItemList, ...$classNameExtList);
                }
                if(strlen($protoClassMdStr) > 0) $protoContent .= "\n".$protoClassMdStr;
            }
        }
        $protoContent       .= "\n".getProtoFileList([$protoFilePath]);
        $postData           = [
            'api_key'       => $docApiKey,
            'api_token'     => $docApiToken,
            'cat_name'      => '定义',
            'page_title'    => strlen($protoFileName) > 0 ? $protoFileName : basename($protoFilePath),
            'page_content'  => $protoContent,
            's_number'      => ++$pageNumDef,
        ];
        $logText            = str_pad($protoShowPath, 64, ' ', STR_PAD_RIGHT);
        $logText            .= "\n";
        $param              = ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => $postData];
        $urlInfo            = CommNet::getUrlInfo($docApiHttp, $param);
        $body               = trim($urlInfo['body'] ?? '');
        $resData            = CommJson::decodeArray($body);
        if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
            ShellPrint::printString($logText, ShellPrint::COLOR_BLUE);
        }else{
            ShellPrint::printErrorString($logText.print_r($resData, true)."\n");
        }
    }
}
// WS通信事件格式
$protoFilePath      = [];
CommFile::getAllFileList(Boot::getConfPath().'ProtoSrc/VO/Socks/EventType/', $protoFilePath);
CommFile::getAllFileList(Boot::getConfPath().'ProtoSrc/Enum/Socks/', $protoFilePath);
$EnumClassName      = SocketEventTypeEnum::class;
$comment            = '';
$wsEventContent     = '**时间：** `'.$currTimeStr.'`';
$wsEventContent     .= "\n".getRefEnumMdString($EnumClassName, $comment);
$wsEventContent     .= "\n".getProtoFileList($protoFilePath);
$pageTitleShow      = 'WS通信事件格式';
$urlInfo            = CommNet::getUrlInfo($docApiHttp, ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => ['cat_name' => '定义', 's_number' => ++$pageNumDef, 'page_title' => $pageTitleShow, 'page_content' => $wsEventContent, 'api_key' => $docApiKey, 'api_token' => $docApiToken]]);
$body               = trim($urlInfo['body'] ?? '');
$resData            = CommJson::decodeArray($body);
if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
    ShellPrint::printlnString($pageTitleShow, ShellPrint::COLOR_CYAN);
}else{
    ShellPrint::printErrorString($pageTitleShow.print_r($resData, true)."\n");
}
// 应用启动协议
$protoFilePath      = [];
CommFile::getAllFileList(Boot::getConfPath().'ProtoSrc/VO/Socks/EventType/', $protoFilePath);
CommFile::getAllFileList(Boot::getConfPath().'ProtoSrc/Enum/Socks/', $protoFilePath);
$toUrlProtocol      = ToUrlCtl::$protocol;
$comment            = '';
$toUrlContent       = <<<TO_URL_EOF
**时间：** `{$currTimeStr}`

```
eg:
$toUrlProtocol
{$toUrlProtocol}home
{$toUrlProtocol}user/info
{$toUrlProtocol}user/info?_json_=urlEncode({jsonStr})
{$toUrlProtocol}user/info?_json_=urlEncode({jsonStr})&paramName=ParamValue

{$toUrlProtocol}{PageName}?_json_=urlEncode(jsonStr)
```
TO_URL_EOF;
// 加载-跳转协议定义
$toUrlList              = [];
foreach((new ReflectionClass(ToUrlCtl::class))->getConstants() as $pageKey => $pageUrl){
    $toUrlList[$pageKey]= ['url' => $pageUrl, 'comment' => ''];
}
$toUrlFileList          = @file(dirname(__DIR__, 3).'/'.preg_replace('"\\\\"', '/', ToUrlCtl::class).'.php');
foreach($toUrlFileList as $line){
    if(preg_match('/\s*public\s+const\s+(U_\S+)\s*=\s*[\'"]([^;]+)[\'"];\s*\/\/\s*(\S+)/', $line, $match)){
        $pageKey        = trim($match[1]);
        $comment        = trim($match[3]);
        if(isset($toUrlList[$pageKey]) && strlen($comment) > 0) $toUrlList[$pageKey]['comment'] = $comment;
    }
}
if(count($toUrlList) > 0){
    $toUrlContent       .= "\n\n".'| PageName | Remark Comment |'."\n".'|:--|:--|'."\n";
    foreach($toUrlList as $pageItem){
        $pageUrl        = strval($pageItem['url']??'');
        if(strlen($pageUrl) < 1) continue;
        $comment        = strval($pageItem['comment']??'');
        $comment        = @preg_replace(['/(@?)\[/', '"]"'], [' `$1[', ']`'], $comment);
        $toUrlContent   .= '| '.$pageUrl.' | '.$comment.' |'."\n";
    }
}
$pageTitleShow      = '应用启动协议';
$urlInfo            = CommNet::getUrlInfo($docApiHttp, ['TIMEOUT' => 60, 'METHOD' => 'POST', 'POST_DATA' => ['cat_name' => '定义', 's_number' => ++$pageNumDef, 'page_title' => $pageTitleShow, 'page_content' => $toUrlContent, 'api_key' => $docApiKey, 'api_token' => $docApiToken]]);
$body               = trim($urlInfo['body'] ?? '');
$resData            = CommJson::decodeArray($body);
if(isset($resData['error_code']) && intval($resData['error_code']) === 0){
    ShellPrint::printlnString($pageTitleShow, ShellPrint::COLOR_CYAN);
}else{
    ShellPrint::printErrorString($pageTitleShow.print_r($resData, true)."\n");
}

echo "\n\n";
